home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume11 / watcher / part02 < prev    next >
Encoding:
Internet Message Format  |  1987-09-27  |  34.4 KB

  1. Subject:  v11i083:  Watcher system monitor program, Part02/02
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rs@uunet.UU.NET
  5.  
  6. Submitted-by: Kenneth Ingham <ingham@charon.unm.edu>
  7. Posting-number: Volume 11, Issue 83
  8. Archive-name: watcher/part02
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then unpack
  12. # it by saving it into a file and typing "sh file".  To overwrite existing
  13. # files, type "sh file -c".  You can also feed this as standard input via
  14. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  15. # will see the following message at the end:
  16. #        "End of archive 2 (of 2)."
  17. # Contents:  Docs/Paper Docs/watcher.1 control.y defs.h
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. if test -f Docs/Paper -a "${1}" != "-c" ; then 
  20.   echo shar: Will not over-write existing file \"Docs/Paper\"
  21. else
  22. echo shar: Extracting \"Docs/Paper\" \(13821 characters\)
  23. sed "s/^X//" >Docs/Paper <<'END_OF_Docs/Paper'
  24. X.TI Keeping watch over the flocks
  25. X.TI by night (and day)
  26. X.AU 7
  27. XKenneth Ingham
  28. XUniversity of New Mexico Computing Center
  29. XDistributed Systems Group
  30. X2701 Campus NE
  31. XAlbuquerque, NM 87131
  32. X(505) 277-8044
  33. Xingham@charon.unm.edu  or ucbvax!unmvax!charon!ingham
  34. X.AB
  35. XOver the last several years, the number of machines maintained by the
  36. XUniversity of New Mexico Computing Center has increased rapidly, yet
  37. Xthe number of system managers monitoring these systems has remained
  38. Xstatic.  Consequently, the system managers were faced with the task of
  39. Xwatching more and more machines; since only one system manager is on
  40. Xcall at any time (known affectionately as "DOC"), this soon proved to
  41. Xbe an unacceptable situation.  Shell scripts running every six hours
  42. Xgave some assistance; this was offset by the fact that the scripts
  43. Xgenerated a great deal of output indicating normal system operation,
  44. Xwhich the system manager still had to scan carefully for signs of
  45. Xtrouble.  This paper describes \fIwatcher\fR, a flexible system monitor
  46. Xwhich watches the system more closely than the human system manager
  47. Xwhile generating less output for him to examine.
  48. X.sp
  49. XRunning more often than the above mentioned set of shell scripts,
  50. X\fIwatcher\fR is able to keep closer tabs on the system; since it
  51. Xdelivers only a list of potential problems, however, this extra
  52. Xmonitoring produces \fIno\fR corresponding increase in the demand on
  53. XDOC.  No problems slip by unnoticed in the more concise output,
  54. Xleading to an improvement in overall system availability as well as the
  55. Xmore effective utilization of the system manager's time.
  56. X.BD
  57. X.SE 0. Acknowledgments (I couldn't have done it without you)
  58. XI would like to thank Leslie Gorsline for her assistance in the writing
  59. Xof this paper.  Without her, this paper might not have been.  Also
  60. Xthanks to the UNMCC distributed systems group for their comments that helped
  61. Ximprove \fIwatcher\fR.
  62. X.SE 1. Background (the problem)
  63. XThe computing facilities offered by the University of New Mexico
  64. XComputing Center (UNMCC) include three microvaxen, five large vaxen
  65. X(780 or bigger), and a Sequent B8000.  In addition to these Unix/VMS
  66. Xmachines, the UNMCC Distributed Systems Group (DSG) monitors a number
  67. Xof the various microvaxen and sun workstations scattered across
  68. Xcampus.  This duty falls to the DSG Programmer designated as "DOC", or
  69. X"DSG On Call", who receives his beeper based on a monthly rotation
  70. Xschedule.
  71. X.sp
  72. XIn the past, shell scripts running every six hours reported various
  73. Xsystem statistics to DOC, who then scanned the output for signs of
  74. Xpossible trouble.  The output of these shell scripts became
  75. Xoverwhelming as the number of machines and potential problems grew;
  76. Xcorresponding to this increase in output was an increase in the amount
  77. Xof time that DOC had to spend reading this output.  In addition, most
  78. Xof this output merely indicated normal system operation; potential
  79. Xproblems were buried amongst non-problems.  Because of this, DOC could
  80. Xoften waste a tremendous amount of time wading through system status
  81. Xreports, time which can be better spent actually fixing system
  82. Xproblems.
  83. X.sp
  84. XUnix is equipped with many powerful tools for program development, but
  85. Xnone which simply watch the system for signs of trouble.  Programs like
  86. X\fIps\fR and \fIdf\fR provide information regarding the current state
  87. Xof the machine, yet it still remains DOC's responsibility to interpret
  88. Xthis information and assess the health of the system at any given
  89. Xtime.  This deficiency can be rectified by providing the
  90. Xsystem with the capacity to determine its own state of health, advising
  91. XDOC when it notices a problem which requires DOC's intervention.
  92. X.SE 2. Design Goals (devising the solution)
  93. XIn designing \fIwatcher\fR, the author closely examined just what DOC
  94. Xdoes in monitoring the system; just how \fIdoes\fR DOC spot potential
  95. Xtrouble in the DOC reports?  These reports consist of output from \fIdf
  96. X-i\fR, \fIruptime\fR, \fIps -aux | sort\fR, and the tail of
  97. X\fIcronlog\fR, which usually only changes in the middle of the night.
  98. XIt was determined that DOC's task consisted primarily of scanning
  99. Xvarious numbers in this output, deciding whether or not they had
  100. Xexceeded an allowable maximum or minimum, or if the values had changed
  101. Xtoo much from the last time the command was run, assuming the last
  102. Xvalue is even remembered.  Getting a computer to do this is more
  103. Xcomplicated than might seem at first glance, due to inconsistencies in
  104. Xthe location of pertinent information between runs of these commands.
  105. XFor instance, the process occupying the fifth line of \fIps -ax\fR
  106. Xmight next time appear on the eighth line; similarly, \fIuptime\fR does
  107. Xnot consistently put germane information in the same place on the line.
  108. X.sp
  109. XWhile flexibility is certainly a primary design consideration, it is
  110. Xnot the whole story.  In order to improve DOC's effectiveness, the
  111. Xprogram should run frequently, roughly every two or three hours,
  112. Xcatching problems early (hopefully before they have affected
  113. Xthe users).  Thus, the program should also be as silent as possible
  114. Xexcept when it detects a potential problem; any advantage DOC gains in
  115. Xusing \fIwatcher\fR would be eliminated if the program delivered an
  116. Xexceedingly verbose status report every two hours.  \fIwatcher\fR's
  117. Xproblem reports should be exact and concise, leading DOC immediately to
  118. Xthe trouble.
  119. X.sp
  120. XThe problem of reducing the amount of output DOC must process can be
  121. Xapproached in different ways, including the redesign of the current
  122. Xshell scripts.  A simple \fIawk\fR script can watch the output from
  123. X\fIdf\fR [1].  However, each command would require a custom tailored
  124. X\fIawk\fR script to look at it.  This task grows more complicated as the
  125. Xnumber of programs running increases.
  126. XWhile a program could be written to
  127. Xgenerate these \fIawk\fR scripts, this process is needlessly complex;
  128. Xfor only a bit more work, an efficient C program such as
  129. X\fIwatcher\fR can be developed.  
  130. X.SE 3. Design (actual implementation of the solution)
  131. XRun at intervals specified in \fIcrontab\fR, \fIwatcher\fR parses a
  132. Xcontrol file (./\fIwatcherfile\fR by default)
  133. Xwith a \fIyacc\fR generated parser, building a data structure
  134. Xcontaining all of the information from the file.  The file contains the
  135. Xlist of commands \fIwatcher\fR
  136. Xshould run (the pipeline), output specifications
  137. Xfor each command (the output format), and the guidelines used in
  138. Xdetermining if something is amiss and should be reported to DOC (the
  139. Xchange format).  A sample \fIwatcher\fR control file would look
  140. Xsomething like this (comment lines begin with a '#'):
  141. X.EX
  142. X# Here is the pipeline and its alias:
  143. X(df -i | /usr/ucb/tail +2) { df }
  144. X# the output format; this is a column output format:
  145. X    $1-9 device%k $41-42 spaceused%d $64-65 inodesused%d:
  146. X# and the change format:
  147. X        spaceused 15%;
  148. X        spaceused 0 89;
  149. X        inodesused 15%;
  150. X        inodesused 0 49.
  151. X
  152. X# another command example:
  153. X(/usr/ucb/ruptime | fgrep -f UnmHosts) { ruptime }
  154. X# this is a relative output format
  155. X    2 status%s 1 machine%k 7 loadav%d:
  156. X# and another change format:
  157. X        loadav 0 10;
  158. X        status "up".
  159. X.NX
  160. XThe first entry causes \fIwatcher\fR to run the \fIdf\fR pipeline
  161. Xlisted in parentheses.  When reporting problems, \fIwatcher\fR refers
  162. Xto this command by the alias provided in the braces; if no alias
  163. Xappears, \fIwatcher\fR uses the entire pipeline.  
  164. X.sp
  165. XThe output format
  166. Xinstructs \fIwatcher\fR how to parse the output;
  167. Xcolumn format, indicated in the output format by \fBnum-num\fR,
  168. Xinstructs \fIwatcher\fR that the output should be parsed
  169. Xby columns, while relative format, denoted by a single integer, shows
  170. Xthat the output should be broken up by whitespaces.
  171. XThrough the convention \fBname%type\fR, the output format also names each
  172. Xfield, indicating whether the field is numeric, string, or
  173. Xkeyword, specified by \fBd\fR, \fBs\fR, or \fBk\fR respectively.
  174. XKeyword fields are
  175. Xused to match up corresponding output lines between runs.  Thus
  176. X.EX
  177. X    41-42 spaceused%d
  178. X.NX
  179. Xindicates that this field, named \fBspaceused\fR, contains numeric 
  180. Xinformation in columns 41-42, while
  181. X.EX
  182. X    2 status%s
  183. X.NX
  184. Xinforms \fIwatcher\fR that the second word (group of non-whitespace
  185. Xcharacters) on the line is a string field named \fBstatus\fR.
  186. XFor the \fIdf\fR example given above,
  187. X.EX
  188. XFilesystem    kbytes    used   avail  capacity   iused   ifree  %iused  Mounted on
  189. X/dev/hp1f      52431   39763    7424    84%    6937    9447    42%   /develop
  190. X.NX
  191. X\fBdevice\fR would be \fI/dev/hp1f\fR, \fBspaceused\fR would be 84,
  192. Xand \fBinodesused\fR would be 42.  Similarly, the output from the
  193. X\fIruptime\fR example, which looks like this
  194. X.EX
  195. Xcharon        up 26+07:53,    17 users,  load 3.12, 2.90, 2.66
  196. X.NX
  197. Xwould be broken at the following places:
  198. X.EX
  199. Xcharon | up | 26+07:53, | 17 | users, | load | 3.12, | 2.90, | 2.66,
  200. X.NX
  201. Xassigning "up" to \fBstatus\fR, and 3.12 to \fBloadav\fR.
  202. X.sp
  203. XThe name field also appears in the change format, designating allowable
  204. Xvalues for this field to have.  These values can be specified as 
  205. Xsingle character strings in the case of string fields; in the case of
  206. Xnumeric fields, the values take the form of either
  207. Xpercentage or absolute changes, or a minimum and maximum which delineate
  208. Xan acceptable range.
  209. XThus
  210. X.EX
  211. X    inodesused 15%;
  212. X    inodesused 0 49.
  213. X.NX
  214. Xsignifies that DOC should be notified if the field named \fBinodesused\fR
  215. Xincreases by more than 15% from the last run, or if it is outside the
  216. Xrange 0 to 49; similarly
  217. X.EX
  218. X    status "up";
  219. X.NX
  220. Xinforms \fIwatcher\fR to notify DOC if the \fBstatus\fR field contains
  221. Xanything other than the word "up".
  222. X.sp
  223. XAs \fIwatcher\fR parses the output of a pipeline, it stores the
  224. Xpertinent parts of the output in a history file (by
  225. Xdefault, ./\fIwatcher.history\fR).
  226. XThe next time \fIwatcher\fR runs, it reads this file to provide
  227. Xcomparison values for the command.  If a command is new (i.e. it has no
  228. Xpreviously-stored output in the history file), \fIwatcher\fR checks the
  229. Xfields which require no previous data, such as min-max fields, while
  230. Xstill storing \fIall\fR of the relevant information to the history file.
  231. XThus, the next time the new command is run, it will be an \fIold\fR command,
  232. Xand meaningful between-run comparisons can be made.
  233. X.sp
  234. XWhen \fIwatcher\fR
  235. Xdetects no problems with the system, DOC receives an empty mail message
  236. Xwith the subject "\fIhostname\fR had no problems at \fIdate\fR";
  237. Xthis is to insure that \fImail\fR is running correctly.  
  238. XWhen it notices a problem which should be brought to DOC's attention,
  239. Xit mails the system problem report in a concise
  240. Xformat, explaining what is wrong and why.  
  241. XThus, rather than the megabytes of shell script output that DOC
  242. Xused to receive and have to read,
  243. Xhe merely sees this when he reads his mail:
  244. X.EX
  245. XMail version 5.2 6/21/85.  Type ? for help.
  246. X"/usr/spool/mail/ingham": 5 messages 5 new
  247. X N  1 root@charon.unm Sat Apr 11 16:00  8/212  "charon had no problems at Sat"
  248. X N  2 root@ariel.unm Sat Apr 11 16:00  8/208  "ariel had no problems at Sat "
  249. X N  3 root@geinah.unm Sat Apr 11 16:00  11/417 "System problem report for gei"
  250. X N  4 root@izar.unm Sat Apr 11 16:00  8/204  "izar had no problems at Sat A"
  251. X N  5 root@deimos.unm Sat Apr 11 16:00  8/212  "deimos had no problems at Sat"
  252. X.NX
  253. XThe letters indicating no problems can be immediately deleted, and DOC
  254. Xcan turn his attention to the letter indicating a 
  255. Xsystem problems.  A sample problem report
  256. Xwould look something like this:
  257. X.EX
  258. Xdf has a max/min value out of range:
  259. X/dev/hp0h     140488  111195   15244    91%   10145   28767    26%   /usr
  260. Xwhere spaceused = 91.00; valid range 0.00 to 89.00.
  261. XAlso it had inodesused change by more than 10%.
  262. XPrevious value 20.00; current value 26.00.
  263. X.NX
  264. XNote that if a line has more than one indication of a problem, all
  265. Xanomalies are included in the report.
  266. XThis provides DOC with as much information as possible, allowing him
  267. Xto determine the problem quickly and devise 
  268. Xa rapid fix (hopefully before users know something is amiss).  
  269. X.sp
  270. X.SE 4. Results (how its helped us)
  271. X\fIwatcher\fR's primary advantage lies in the reduction of DOC's work
  272. Xload.  It has taken over the more menial aspects of monitoring a system,
  273. Xtasks like reading and comparing numbers, 
  274. Xgiving DOC more time to concentrate on bugs of a nature which
  275. X\fIwatcher\fR isn't set up to monitor, such as problems in the
  276. Xaccounting system.
  277. XDOC is apprised of potential problems quickly, and in
  278. Xsome cases can repair them in less time than simply
  279. Xreading the shell script output
  280. Xwould have taken.
  281. X.sp
  282. XThe ability to monitor changes between runs has also helped bring to our
  283. Xattention some
  284. Xproblems which were missed in the DOC reports.  For example,
  285. Xdisk space on \fI/u2\fR on one of our machines jumped by more than 15%.  Since
  286. Xthis jump did not force the total space used above 90%, at which point
  287. XDOC would have investigated the filesystem, it is unlikely
  288. Xthat DOC would have even noticed this sudden change.  The facility to
  289. Xwatch for relative changes between runs enables DOC to catch problems in
  290. Xtheir infancy, and fix problems such as filesystems filling up too
  291. Xrapidly before they inconvenience the users.
  292. X.sp
  293. XSince the system manager specifies not only the commands \fIwatcher\fR will
  294. Xexecute and the time lapse between successive runs, but also the
  295. Xparameters which indicate system anomalies,
  296. X\fIwatcher\fR can easily be seen as a very flexible, general system
  297. Xmonitor.  Its use at UNM has provided an increase in the
  298. Xproductivity of the system manager, which has led in turn to the
  299. Xincrease in the reliability and availability of the systems at UNMCC.
  300. X.SE 5. Availability (how to get one)
  301. X\fIwatcher\fR will be sent to the moderator of mod.sources after the
  302. Xconference is over.
  303. X.SE 6. References (you might also find this interesting)
  304. X.in +0.5i
  305. X.ti -0.5i
  306. X[1] Monitoring Free Disk Space, Rik Farrow, Wizard's Grabbag, \fIUnix
  307. XWorld\fR, Vol. IV, no. 3, pp. 86-87.
  308. X.in -0.5i
  309. END_OF_Docs/Paper
  310. if test 13821 -ne `wc -c <Docs/Paper`; then
  311.     echo shar: \"Docs/Paper\" unpacked with wrong size!
  312. fi
  313. # end of overwriting check
  314. fi
  315. if test -f Docs/watcher.1 -a "${1}" != "-c" ; then 
  316.   echo shar: Will not over-write existing file \"Docs/watcher.1\"
  317. else
  318. echo shar: Extracting \"Docs/watcher.1\" \(6175 characters\)
  319. sed "s/^X//" >Docs/watcher.1 <<'END_OF_Docs/watcher.1'
  320. X.de EX
  321. X.nf
  322. X.sp
  323. X.in +0.5i
  324. X..
  325. X.de NX
  326. X.sp
  327. X.in -0.5i
  328. X.ad
  329. X.fi
  330. X..
  331. X.TH WATCHER 1 "April 10, 1987"
  332. X.UC 6
  333. X.ad
  334. X.SH NAME
  335. Xwatcher \- system monitoring program
  336. X.SH SYNOPSIS
  337. X.B watcher
  338. X[ \-p] [ \-v ]
  339. X[ \-h histfile ] [ \-f controlfile ]
  340. X.SH DESCRIPTION
  341. X.I Watcher
  342. Xis a program to watch the system, reporting only when it finds something
  343. Xamiss.
  344. X.I Watcher
  345. Xreads commands from
  346. X.I controlfile
  347. Xto determine what to watch, the output format of the commands it is to
  348. Xrun, and the acceptable limits for the output of those commands.
  349. XIf no
  350. X.B \-f
  351. Xoption is present, the program looks first for `watcherfile',
  352. Xthen `Watcherfile' to use as the input.
  353. X.PP
  354. XThe
  355. X.B \-h histfile
  356. Xflag tells watcher what file to use as a history file for comparisons
  357. Xbetween runs.  The default is `watcher.history'.
  358. X.PP
  359. XThe
  360. X.B \-p
  361. Xoption has
  362. X.I watcher
  363. Xpretty print the control file.  This is useful to make sure that watcher
  364. Xis parsing the file the way expected, and to provide a prettier version
  365. Xof the control file to use (i.e. it is of limited use).
  366. X.PP
  367. XThe
  368. X.B \-v
  369. Xoption tells watcher to be verbose as it is running.  It will print out
  370. Xvarious information about where it is looking for the files that it
  371. Xuses, the commands that it is executing, and the output from these
  372. Xcommands.  This option is mainly of use when debugging control files or
  373. Xdebugging watcher itself.
  374. X.PP
  375. X.I Watcherfile
  376. Xcontains a sequence of entries that specify the commands to be executed,
  377. Xthe output format of those commands, and what changes should be
  378. Xreported.  The format of the control file is one or more of the
  379. Xfollowing:
  380. X.EX
  381. X( <pipeline> ) { <alias> }
  382. X    <output format> :
  383. X    <change format>.
  384. X.NX
  385. XA <pipeline> is a series of commands joined together with pipes ('|').
  386. XThis command is executed and the output parsed according to the output
  387. Xformat specified.  It is then checked against the change format for
  388. Xpotential problems.  An <alias> is optional; it is used when identifying
  389. Xthe command in the report of problems encountered.  If there is no
  390. Xalias, the entire pipeline is used.  The reason for using an alias is
  391. Xto keep the report clean; the pipelines tend to be long and messy.
  392. X.PP
  393. XAn <output format> is either a column format or a relative format.  A
  394. Xcolumn format is one or more of the following:
  395. X.EX
  396. X    <start> - <end> <name> % <type> 
  397. X.NX
  398. XWhere <start> is the first column containing the information to be compared and
  399. X<end> is the last one.  <name> is the name of the field.  This name is
  400. Xmatched with the names in the change format to identify where in the
  401. Xoutput the appropriate information is.  <type> is either "d", "s", or
  402. X"k" specifying numeric or string data, or a keyword which is used in
  403. Xmatching output from the various programs between runs.
  404. X.PP
  405. XRelative formats are one or more of the following:
  406. X.EX
  407. X<field> <name> % <type>
  408. X.NX
  409. XWhere <field> is the field on the line (a field is defined as a sequence
  410. Xof non-whitespace surrounded by whitespace).  <name> and <type> are the
  411. Xsame as for above.
  412. X.PP
  413. XA change format consists of various names and what changes are
  414. Xallowable.  Change format entries are separated by semicolons (';').  The
  415. Xlist of change formats is terminated by a period ('.').  A semicolon
  416. Xdoes not follow the last change format.
  417. X.I Watcher
  418. Xknows about 4 types of changes.  It can compare the output (numeric)
  419. Xto the previous value and calculate the percentage change.  If the
  420. Xchange is greater that a set amount, a message is generated.  The syntax
  421. Xof this format is:
  422. X.EX
  423. X    <name> <value> % 
  424. X.NX
  425. Xwhere <name> is a name matching a name in the output format and <value
  426. Xis the maximum percentage change which is allowed before a report is
  427. Xissued.
  428. X.PP
  429. XVery similar to the percentage change is the absolute change.  The only
  430. Xdifference is that a percentage is not calculated.  The difference is
  431. Xcalculated and compared to the value given.  Values greater that what is
  432. Xprovided are reported.  The syntax is:
  433. X.EX
  434. X    <name> <value>
  435. X.NX
  436. X.PP
  437. XA maximum and minimum may be specified for numeric data also.  This is
  438. Xuseful for only numeric data.  The format for this is:
  439. X.EX
  440. X    <name> <max> <min>
  441. X.NX
  442. X.PP
  443. X.I Watcher
  444. Xcan also watch for string values changing from a given value to any
  445. Xother value.  This syntax is:
  446. X.EX
  447. X    <name> "<value>"
  448. X.NX
  449. X.PP
  450. XA sample control file is provided below:
  451. X.EX
  452. X(df -i | /usr/ucb/tail +2) { df }
  453. X    1-9 filesystem%k 41-42 spaceused%d 64-65 inodesused%d 1-9 device%k:
  454. X        spaceused 15%;
  455. X        spaceused 0 89;
  456. X        inodesused 15%;
  457. X        inodesused 0 49.
  458. X(/usr/ucb/ruptime | fgrep -f UnmHosts) { ruptime }
  459. X    2 status%s 1 machine%k 7 loadav%d:
  460. X        loadav 0 10;
  461. X        status "up".
  462. X(ps -aux | fgrep -v -f Daemons | /usr/ucb/tail +2) { 'ps with no daemons' }
  463. X    9-14 pid%k 16-19 percentcpu%d 42-45 cputime%d:
  464. X        cputime 0 10.
  465. X.NX
  466. XNote that there is no order for the output format specifiers; the second
  467. Xfield may be specified before the first.  
  468. X.PP
  469. XAll names are of arbitrary length, start with [a-zA-Z] and contain no
  470. Xwhite space unless enclosed in tics ("'").
  471. X.PP
  472. XThe pipeline is executed by 
  473. X.I popen(3),
  474. Xwhich uses 
  475. X.I sh(1)
  476. Xto expand the command; therefore shell metacharacters may be used.
  477. X.PP
  478. XThe control file may have comments in it.  Comments are delimited by a #
  479. Xon the left and a newline on the right.
  480. X.SH FILES
  481. X.nf
  482. XWatcherfile or watcherfile    default control file.
  483. Xwatcher.history            default file containing results of previous run.
  484. X.SH AUTHOR
  485. X.nf
  486. XKenneth Ingham
  487. XUniversity of New Mexico Computing Center
  488. X2701 Campus NE
  489. XAlbuquerque, NM, 87131
  490. Xingham@charon.unm.edu
  491. X.SH "SEE ALSO"
  492. Xpopen(3), sh(1),
  493. X.I Keeping Watch over the 
  494. X.I Flocks by Night (and day)
  495. Xby Kenneth Ingham, Summer 1987 Usenix proceedings.
  496. X.SH DIAGNOSTICS
  497. XFiles which can't be opened cause a message about which files couldn't
  498. Xbe found and the program exits.
  499. X.sp
  500. XThere are various syntax errors when parsing the controlfile.  These
  501. Xalso cause an exit.
  502. X.sp
  503. X.I Watcher
  504. Xcomplains when output does not parse according to the format
  505. Xprovided.  It will continue to look at the rest of the output.
  506. X.SH BUGS
  507. XSyntax errors in control file are not easy to find based on the error
  508. Xmessages.
  509. X.sp
  510. XDoesn't warn when a string variable has been selected for a numeric
  511. Xcomparison.
  512. X.sp
  513. XOnly notices changes which get bigger; things which suddenly
  514. Xdrop will not be noticed.
  515. END_OF_Docs/watcher.1
  516. if test 6175 -ne `wc -c <Docs/watcher.1`; then
  517.     echo shar: \"Docs/watcher.1\" unpacked with wrong size!
  518. fi
  519. # end of overwriting check
  520. fi
  521. if test -f control.y -a "${1}" != "-c" ; then 
  522.   echo shar: Will not over-write existing file \"control.y\"
  523. else
  524. echo shar: Extracting \"control.y\" \(6895 characters\)
  525. sed "s/^X//" >control.y <<'END_OF_control.y'
  526. X/*
  527. X   the grammer describing the control file for watcher.
  528. X
  529. X   Kenneth Ingham
  530. X
  531. X   Copyright (C) 1987 The University of New Mexico
  532. X*/
  533. X
  534. X%token PIPELINE NUMBER STRING
  535. X%start command
  536. X
  537. X%{
  538. X#include "defs.h"
  539. X#define SMAXMIN    struct max_min_st
  540. X#define SOUTFMT    struct out_fmt_st
  541. X#define SCOLOUT    struct col_out_st
  542. X#define SRELOUT    struct rel_out_st
  543. X#define SCMD    struct cmd_st
  544. X#define SCH    struct change_fmt_st
  545. Xextern char *strval, ostrval[], pipeline[];
  546. Xextern int intval, ointval;
  547. Xextern struct cmd_st *clist;
  548. Xint out_type;
  549. Xchar alias[MAX_STR];
  550. Xunion out_fmt_u key;
  551. X%}
  552. X
  553. X%%
  554. X/*
  555. X   THE PARSER DESCRIPTION (read aloud to the beginning of
  556. X   _Also_sprach_Zarathustra_)
  557. X
  558. X   As things are discovered, they are put into the linked list structure
  559. X   detailed in defs.h.  Some error checking is done, but the messages
  560. X   leave a bit to be desired.  Yacc and I didn't get along too well in
  561. X   figuring out how to handle errors.
  562. X
  563. X   Assuming that pointers will fit into YYSTYPE (int on 4.3 vax).
  564. X   Should use yacc's union facility.
  565. X*/
  566. X   
  567. Xcommand        : one_command
  568. X            {
  569. X                if (clist == NULL)
  570. X                    clist = (SCMD *) $1;
  571. X            }
  572. X        | command one_command
  573. X            { 
  574. X                SCMD *p;
  575. X
  576. X                if (clist != NULL) {
  577. X                    for (p=clist; p->next!=NULL; p=p->next)
  578. X                        ;
  579. X                    p->next = (SCMD *)$2;
  580. X                }
  581. X                else
  582. X                    clist = (SCMD *)$1;
  583. X            }
  584. X        | error '.'
  585. X            {
  586. X            fprintf(stderr, "Command error near ");
  587. X            fprintf(stderr, "'%s'\n", pipeline);
  588. X            fprintf(stderr,"Last string read was '%s'\n", strval);
  589. X            }
  590. X        ;
  591. X
  592. Xone_command    : PIPELINE alias out_fmt ':' change_fmt '.'
  593. X            {
  594. X                SCMD *p;
  595. X
  596. X                p = (SCMD *) malloc(sizeof(SCMD));
  597. X                if (p == NULL) {
  598. X                    fprintf(stderr,"malloc error\n");
  599. X                    exit(1);
  600. X                }
  601. X                p->pipeline = malloc((unsigned)strlen(pipeline)+1);
  602. X                if (p->pipeline == NULL) {
  603. X                    fprintf(stderr,"malloc error\n");
  604. X                    exit(1);
  605. X                }
  606. X                (void) strcpy(p->pipeline, pipeline);
  607. X
  608. X                if (alias[0]) {
  609. X                    p->alias = malloc((unsigned)strlen(alias)+1);
  610. X                    if (p->alias == NULL) {
  611. X                        fprintf(stderr,"malloc error\n");
  612. X                        exit(1);
  613. X                    }
  614. X                    (void) strcpy(p->alias, alias);
  615. X                }
  616. X                else
  617. X                    p->alias = NULL;
  618. X
  619. X                p->change_fmt = (SCH *)$5;
  620. X                p->next = NULL;
  621. X                p->out_type = out_type;
  622. X                if (out_type == RELATIVE) {
  623. X                    p->out_fmt.rel_fmt = (SRELOUT *)$3;
  624. X                    p->key.rel_fmt = key.rel_fmt;
  625. X                }
  626. X                else { /* it better be column */
  627. X                    p->out_fmt.col_fmt = (SCOLOUT *)$3;
  628. X                    p->key.col_fmt = key.col_fmt;
  629. X                }
  630. X
  631. X                key.rel_fmt = NULL;
  632. X                $$ = (int)p;
  633. X            }
  634. X        ;
  635. X
  636. Xout_fmt        : rel_out_fmt
  637. X            { 
  638. X                out_type = RELATIVE;
  639. X                $$ = $1;
  640. X            }
  641. X        | col_out_fmt
  642. X            {
  643. X                out_type = COLUMN;
  644. X                $$ = $1;
  645. X            }
  646. X        | error '.'
  647. X            {
  648. X                fprintf(stderr,"Output format error on ");
  649. X                fprintf(stderr,"'%s'\n", pipeline);
  650. X                fprintf(stderr,"Last string read was '%s'\n",
  651. X                    strval);
  652. X            }
  653. X        ;
  654. X
  655. Xchange_fmt    : one_change_fmt
  656. X        | change_fmt ';' one_change_fmt
  657. X            {
  658. X                SCH *p;
  659. X
  660. X                for (p=(SCH *)$1; p->next != NULL; p = p->next)
  661. X                    ;
  662. X                
  663. X                p->next = (SCH *)$3;
  664. X
  665. X                $$ = (int)$1;
  666. X            }
  667. X        ;
  668. X
  669. Xone_change_fmt    : pct_change_fmt
  670. X            { $$ = $1; } /* Is this default? */
  671. X        | abs_change_fmt
  672. X            { $$ = $1; }
  673. X        | max_min_fmt
  674. X            { $$ = $1; }
  675. X        | str_change_fmt
  676. X            { $$ = $1; }
  677. X        | error '.'
  678. X            {
  679. X            fprintf(stderr,"Unknown change format type ");
  680. X            fprintf(stderr,"on '%s'\n", pipeline);
  681. X            fprintf(stderr,"Last string read '%s'\n", strval);
  682. X            }
  683. X        ;
  684. X
  685. Xrel_out_fmt    : one_rel_fmt
  686. X        | rel_out_fmt  one_rel_fmt
  687. X            {
  688. X                SRELOUT *p;
  689. X
  690. X                for (p=(SRELOUT *)$1;p->next != NULL; p=p->next)
  691. X                    ;
  692. X                p->next = (SRELOUT *)$2;
  693. X                $$ = (int)$1;
  694. X            }
  695. X        ;
  696. X
  697. Xcol_out_fmt    : one_col_fmt
  698. X        | col_out_fmt  one_col_fmt
  699. X            {
  700. X                SCOLOUT *p;
  701. X
  702. X                for (p=(SCOLOUT *)$1;p->next != NULL; p=p->next)
  703. X                    ;
  704. X                p->next = (SCOLOUT *)$2;
  705. X                $$ = (int)$1;
  706. X            }
  707. X        /*
  708. X        | error '.'
  709. X            {
  710. X            fprintf(stderr,"Column output format error\n");
  711. X            fprintf(stderr,"Last string read '%s'\n", strval);
  712. X            }
  713. X        */
  714. X        ;
  715. X
  716. Xone_rel_fmt    : NUMBER STRING '%' STRING
  717. X            {
  718. X            extern int parse_error;
  719. X            SRELOUT *p;
  720. X
  721. X            p = (SRELOUT *) malloc(sizeof(SRELOUT));
  722. X            p->name = malloc((unsigned)strlen(ostrval)+1);
  723. X            (void) strcpy(p->name, ostrval);
  724. X            p->field = intval;
  725. X            p->next = NULL;
  726. X
  727. X            if (strval[1] != '\0') {
  728. X                fprintf(stderr,"%s: Invalid type specifier '%s'.\n",
  729. X                    NAME, strval);
  730. X                parse_error = True;
  731. X                $$ = (int)p;
  732. X            }
  733. X
  734. X            switch (*strval) {
  735. X                case 'd':
  736. X                case 'f':
  737. X                    p->type = NUM;
  738. X                    break;
  739. X                case 's':
  740. X                    p->type = STRING;
  741. X                    break;
  742. X                case 'k':
  743. X                    p->type = KEY;
  744. X                    key.rel_fmt = p;
  745. X                    break;
  746. X                default:
  747. X                    fprintf(stderr,"%s: Invalid type specifier '%s'.\n",
  748. X                        NAME, strval);
  749. X                    parse_error = True;
  750. X            }
  751. X
  752. X            $$ = (int) p;
  753. X            }
  754. X        ;
  755. X
  756. Xone_col_fmt    : NUMBER '-' NUMBER STRING '%' STRING
  757. X            {
  758. X                extern int parse_error;
  759. X                SCOLOUT *p;
  760. X
  761. X                p = (SCOLOUT *) malloc(sizeof(SCOLOUT));
  762. X                p->name = malloc((unsigned)strlen(ostrval)+1);
  763. X                (void) strcpy(p->name, ostrval);
  764. X                p->start = ointval;
  765. X                p->end = intval;
  766. X                p->next = NULL;
  767. X
  768. X                if (ointval >= intval) {
  769. X                    fprintf(stderr,"%s: start %d larger than end %d!\n", NAME, ointval, intval);
  770. X                    parse_error = True;
  771. X                    $$ = (int)p;
  772. X                }
  773. X
  774. X                if (strval[1] != '\0') {
  775. X                    fprintf(stderr,"%s: Invalid %s '%s'.\n",
  776. X                        NAME, "type specifier", strval);
  777. X                    parse_error = True;
  778. X                    $$ = (int)p;
  779. X                }
  780. X
  781. X                switch (*strval) {
  782. X                    case 'd':
  783. X                    case 'f':
  784. X                        p->type = NUM;
  785. X                        break;
  786. X                    case 's':
  787. X                        p->type = STRING;
  788. X                        break;
  789. X                    case 'k':
  790. X                        p->type = KEY;
  791. X                        key.col_fmt = p;
  792. X                        break;
  793. X                    default:
  794. X                        fprintf(stderr,"%s: %s '%s'.\n",
  795. X                            NAME,
  796. X                            "Bad type specifier",
  797. X                            strval);
  798. X                        parse_error = True;
  799. X                }
  800. X                $$ = (int) p;
  801. X            }
  802. X        ;
  803. X
  804. Xpct_change_fmt    : STRING NUMBER '%'
  805. X            {
  806. X                SCH *p;
  807. X
  808. X                p = (SCH *) malloc(sizeof(SCH));
  809. X                p->name = malloc((unsigned)strlen(strval)+1);
  810. X                (void) strcpy(p->name, strval);
  811. X                p->fmt.percent = (float)intval / 100;
  812. X                p->type = PERCENT;
  813. X
  814. X                $$ = (int) p;
  815. X            }
  816. X        ;
  817. X
  818. Xabs_change_fmt    : STRING NUMBER
  819. X            {
  820. X              SCH *p;
  821. X
  822. X              p = (SCH *) malloc(sizeof(SCH));
  823. X              p->name = malloc((unsigned) strlen(strval)+1);
  824. X              (void) strcpy(p->name, strval);
  825. X              p->fmt.abs_amount = intval;
  826. X              p->type = ABSOLUTE;
  827. X
  828. X              $$ = (int) p;
  829. X            }
  830. X        ;
  831. X
  832. Xmax_min_fmt    : STRING NUMBER NUMBER
  833. X            {
  834. X              SCH *p;
  835. X
  836. X              p = (SCH *) malloc(sizeof(SCH));
  837. X              p->name = malloc((unsigned)strlen(strval)+1);
  838. X              (void) strcpy(p->name, strval);
  839. X              p->fmt.max_min.max = intval;
  840. X              p->fmt.max_min.min = ointval;
  841. X              p->type = MAX_MIN;
  842. X
  843. X              $$ = (int) p;
  844. X            }
  845. X        ;
  846. X
  847. Xstr_change_fmt    : STRING '"' STRING '"'
  848. X            { 
  849. X              SCH *p;
  850. X
  851. X              p = (SCH *) malloc(sizeof(SCH));
  852. X              p->name = malloc((unsigned)strlen(ostrval)+1);
  853. X              (void) strcpy(p->name, ostrval);
  854. X              p->fmt.str_value = malloc((unsigned)strlen(strval)+1);
  855. X              (void) strcpy(p->fmt.str_value, strval);
  856. X              p->type = STRING;
  857. X
  858. X              $$ = (int) p;
  859. X            }
  860. X        ;
  861. X
  862. Xalias        : '{' STRING '}'
  863. X            { (void) strcpy(alias, strval); }
  864. X        | empty 
  865. X            { alias[0] = '\0'; }
  866. X        ;
  867. X
  868. Xempty        : ;
  869. END_OF_control.y
  870. if test 6895 -ne `wc -c <control.y`; then
  871.     echo shar: \"control.y\" unpacked with wrong size!
  872. fi
  873. # end of overwriting check
  874. fi
  875. if test -f defs.h -a "${1}" != "-c" ; then 
  876.   echo shar: Will not over-write existing file \"defs.h\"
  877. else
  878. echo shar: Extracting \"defs.h\" \(4706 characters\)
  879. sed "s/^X//" >defs.h <<'END_OF_defs.h'
  880. X/*
  881. X  Structure definitions, included files needed and useful constants for
  882. X  Watcher.
  883. X
  884. X  Kenneth Ingham
  885. X
  886. X  Copyright (C) 1987 The University of New Mexico
  887. X*/
  888. X
  889. X#include <stdio.h>
  890. X#include <ctype.h>
  891. X
  892. X#ifdef BSD
  893. X#include <strings.h>
  894. X#else
  895. X#include <string.h>
  896. X#endif
  897. X
  898. X#include <signal.h>
  899. X#include "y.tab.h"
  900. X
  901. X#define False        0
  902. X#define True        1
  903. X#define NAME        "watcher"
  904. X#define MAX_STR        256
  905. X#define DEF_CONTROL    "watcherfile"
  906. X#define DEF_CONTROL2    "Watcherfile"
  907. X#define DEF_HISTFILE    "watcher.history"
  908. X#define MAX_NAME    16
  909. X#define MAX_VEC        100
  910. X
  911. X#define INT        1
  912. X#define FLOAT        2
  913. X#define PERCENT        3
  914. X#define ABSOLUTE    4
  915. X#define MAX_MIN        5
  916. X#define RELATIVE    6
  917. X#define COLUMN        7
  918. X#define NUM        8 /* really needs to be replaced by int or float */
  919. X#define KEY        9
  920. X
  921. X/* 
  922. X   below lie the structure definitions for all of the linked lists used
  923. X   in watcher.  Make piece with your god before venturing onward
  924. X*/
  925. X
  926. X/*
  927. X   what a column output fromat entry looks like:
  928. X*/
  929. Xstruct col_out_st {
  930. X    char *name;            /* name of output field */
  931. X    int start;            /* where it starts... */
  932. X    int end;            /* ... and ends */
  933. X    int type;            /* what type of data to find there */
  934. X    struct col_out_st *next;    /* and the next in the list is... */
  935. X};
  936. X
  937. X/* 
  938. X   and a relative output format...
  939. X*/
  940. Xstruct rel_out_st {
  941. X    char *name;            /* name of output field */
  942. X    int field;            /* Which field to look in for it */
  943. X    int type;            /* what type of data to find there */
  944. X    struct rel_out_st *next;    /* and the next in the list */
  945. X};
  946. X
  947. X/*
  948. X   types of change formats; all joined in a union later.
  949. X*/
  950. Xstruct max_min_st {
  951. X    float max;            /* max allowable value... */
  952. X    float min;            /* ...and the min */
  953. X};
  954. X
  955. Xunion fmt_u {
  956. X    float percent;            /* percent change */
  957. X    float abs_amount;        /* absolute change */
  958. X    struct max_min_st max_min;    /* max and min */
  959. X    char *str_value;        /* what the string should be */
  960. X};
  961. X
  962. X/*
  963. X   the actual structure describing how things are allowed to change.
  964. X*/
  965. Xstruct change_fmt_st {
  966. X    char *name;            /* name of the field this pertains to */
  967. X    int type;            /* what type of change */
  968. X    union fmt_u fmt;        /* the format, depending on type */
  969. X    struct change_fmt_st *next;    /* and of course the next in the list */
  970. X};
  971. X
  972. X/*
  973. X   for the various types of output formats:
  974. X*/
  975. Xunion out_fmt_u {
  976. X    struct rel_out_st *rel_fmt;    /* a relative output format (fields) */
  977. X    struct col_out_st *col_fmt;    /* or one that uses columns */
  978. X};
  979. X
  980. X/* 
  981. X   what we are all about: the command structure.  Here we bring it all
  982. X   together and have something to work with (luckily the subroutines are
  983. X   also set up in a similar heirarchy and we can pass various parts of
  984. X   the linked lists to them and they don't care about the "upper" parts.
  985. X*/
  986. Xstruct cmd_st {
  987. X    char *pipeline;            /* the pipeline to execute */
  988. X    char *alias;            /* a name to use when refering to it */
  989. X    int out_type;            /* what type of output format used */
  990. X    union out_fmt_u out_fmt;    /* the output format */
  991. X    union out_fmt_u key;        /* what to key on for btwn run cmps */
  992. X    struct change_fmt_st *change_fmt;/*the things to watch for changes */
  993. X    struct cmd_st *next;        /* and of course the next in the list */
  994. X};
  995. X
  996. X/*
  997. X   now we get into the structures for the history linked list...
  998. X*/
  999. X
  1000. X/*
  1001. X   a way of storing any data type:
  1002. X*/
  1003. Xunion all_u {
  1004. X    char *strval;
  1005. X    int intval;
  1006. X    float floatval;
  1007. X};
  1008. X
  1009. X/*
  1010. X   which is used here where we hold the value for a specified key value
  1011. X   from the previous output.
  1012. X*/
  1013. Xstruct val_st {
  1014. X    char *name;        /* output field name */
  1015. X    int type;        /* what type of data in the union */
  1016. X    union all_u val;    /* ... the data (wow!) */
  1017. X    struct val_st *next;    /* and where would we be without a next one? */
  1018. X};
  1019. X
  1020. X/* 
  1021. X   for each line in the output of a command, we grab the key and store
  1022. X   all of the values obtained from the line.  Here is how it is done.
  1023. X*/
  1024. Xstruct key_st {
  1025. X    char *key_value;    /* value for the key (could you guess?) */
  1026. X    struct val_st *vals;    /* the various interesting parts of the line */
  1027. X    struct key_st *next;    /* and of course the next one */
  1028. X};
  1029. X
  1030. X/*
  1031. X   inally we come to the reaon for all of the above structures.  This is
  1032. X   how previous commands are stored once they have been read in from the
  1033. X   history file.
  1034. X*/
  1035. Xstruct old_cmd_st {
  1036. X    char *pipeline;            /* the pipeline executed */
  1037. X    struct key_st *keys;        /* the keys and their useful parts */
  1038. X    struct old_cmd_st *next;    /* and the next pipeline... */
  1039. X};
  1040. X
  1041. X/*
  1042. X   way of converting between type stored in struct and char for a
  1043. X   person's use.  This works on the cmd_st.out_type values.
  1044. X*/
  1045. X#define TCHAR(i)    (i==STRING ? 's' : (i == KEY ? 'k' : 'd'))
  1046. X
  1047. Xchar *malloc();        /* really should switch back to malloc */
  1048. Xchar *get_rel_field(), *get_col_field();
  1049. Xdouble atof();
  1050. Xstruct old_cmd_st *find_prev_cmd();
  1051. X
  1052. X/* bezerkeley vs system v differences... */
  1053. X#ifdef SYSV
  1054. X#define index    strchr
  1055. X#endif
  1056. END_OF_defs.h
  1057. if test 4706 -ne `wc -c <defs.h`; then
  1058.     echo shar: \"defs.h\" unpacked with wrong size!
  1059. fi
  1060. # end of overwriting check
  1061. fi
  1062. echo shar: End of archive 2 \(of 2\).
  1063. cp /dev/null ark2isdone
  1064. MISSING=""
  1065. for I in 1 2 ; do
  1066.     if test ! -f ark${I}isdone ; then
  1067.     MISSING="${MISSING} ${I}"
  1068.     fi
  1069. done
  1070. if test "${MISSING}" = "" ; then
  1071.     echo You have unpacked both archives.
  1072.     rm -f ark[1-9]isdone
  1073. else
  1074.     echo You still need to unpack the following archives:
  1075.     echo "        " ${MISSING}
  1076. fi
  1077. ##  End of shell archive.
  1078. exit 0
  1079.